﻿using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;

namespace BToolkit
{
    public class QRCodeUtils : MonoBehaviour
    {
        private static QRCodeUtils _instance;
        public static QRCodeUtils Instance
        {
            get
            {
                if (!_instance)
                {
                    _instance = GameObject.FindObjectOfType<QRCodeUtils>();
                    if (!_instance)
                    {
                        GameObject go = new GameObject("QRCodeUtils");
                        DontDestroyOnLoad(go);
                        _instance = go.AddComponent<QRCodeUtils>();
                    }
                }
                return _instance;
            }
        }
        Action createAction, scanAction;
        BarcodeReader barcodeReader;
        WebCamTexture cameraTexture;
        Color32[] colors;
        bool isAppPlaying, isScaning;

        private void Awake()
        {
            barcodeReader = new BarcodeReader();
        }

        private void OnEnable()
        {
            isAppPlaying = true;
        }

        private void OnDisable()
        {
            isAppPlaying = false;
        }

        private void Update()
        {
            if (cameraTexture != null)
            {
                colors = cameraTexture.GetPixels32();
            }
            if (createAction != null)
            {
                createAction();
                createAction = null;
            }

            if (scanAction != null)
            {
                scanAction();
                scanAction = null;
            }
        }

        /// <summary>
        /// 生成二维码(用QrCodeEncodingOptions的方法只能生成256尺寸，故用此自定义方法)
        /// </summary>
        public void CreateQRCode(string content, int width, int height, Action<Texture2D> callback)
        {
            new Thread(() =>
            {
                MultiFormatWriter writer = new MultiFormatWriter();
                Dictionary<EncodeHintType, object> hints = new Dictionary<EncodeHintType, object>();
                hints.Add(EncodeHintType.CHARACTER_SET, "UTF-8");
                hints.Add(EncodeHintType.MARGIN, 1);
                hints.Add(EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.M);
                BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);

                // 转成texture2d
                int w = bitMatrix.Width;
                int h = bitMatrix.Height;

                Color32[] color32 = Encode(content, width, height);
                createAction = () =>
                {
                    Texture2D texture = new Texture2D(w, h);
                    for (int x = 0; x < h; x++)
                    {
                        for (int y = 0; y < w; y++)
                        {
                            if (bitMatrix[x, y])
                            {
                                //标志色
                                texture.SetPixel(y, x, Color.black);
                            }
                            else
                            {
                                //底色
                                texture.SetPixel(y, x, Color.white);
                            }
                        }
                    }
                    texture.Apply();
                    callback?.Invoke(texture);
                };
            }).Start();
        }

        /// <summary>
        /// 扫描二维码 duration:
        /// 注意：输入的图片分辨率太高反而难识别，小图有一定的模糊度更容易识别
        /// </summary>
        /// <param name="duration">扫描时长,传小于或等于0表示永久扫描直到调用StopScan()方法（秒）</param>
        /// <param name="callback">返回任何内容都会自动停止扫描</param>
        public void ScanQRCode(WebCamTexture texture, float duration, Action<string> callback)
        {
            if (isScaning) return;
            if (!texture)
            {
                Debuger.LogError("texture 为 null");
                return;
            }
            cameraTexture = texture;
            int width = texture.width;
            int height = texture.height;
            new Thread(() =>
            {
                Result result = null;
                TimeSpan beginTime = new TimeSpan(DateTime.Now.Ticks);
                isScaning = true;
                while (isScaning && result == null)
                {
                    if (colors != null) result = barcodeReader.Decode(colors, width, height);
                    double time = (new TimeSpan(DateTime.Now.Ticks) - beginTime).TotalSeconds;
                    if (duration > 0 && time > duration) break;
                    if (time > 1 && !isAppPlaying) break;
                }
                isScaning = false;
                cameraTexture = null;
                colors = null;
                scanAction = () =>
                {
                    if (result != null)
                    {
                        callback?.Invoke(result.Text);
                    }
                    else
                    {
                        callback?.Invoke(null);
                    }
                };
            }).Start();
        }

        /// <summary>
        /// 立即停止扫描,原来扫描时传入的回调将直接返回null
        /// </summary>
        public void StopScan()
        {
            isScaning = false;
        }

        private static Color32[] Encode(string textForEncoding, int width, int height)
        {
            BarcodeWriter writer = new BarcodeWriter
            {
                Format = BarcodeFormat.QR_CODE,
                Options = new QrCodeEncodingOptions
                {
                    Height = height,
                    Width = width
                }
            };
            return writer.Write(textForEncoding);
        }

    }

}